home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / smail-3.1.28 / src / spool.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-11  |  47.9 KB  |  1,794 lines

  1. /* @(#)src/spool.c    1.8 7/11/92 11:51:10 */
  2.  
  3. /*
  4.  *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  5.  *    Copyright (C) 1992  Ronald S. Karr
  6.  * 
  7.  * See the file COPYING, distributed with smail, for restriction
  8.  * and warranty information.
  9.  */
  10.  
  11. /*
  12.  * spool.c:
  13.  *    message spooling and retrieval.  This source file implements a
  14.  *    reliable spooling system which is resiliant against inaccessible
  15.  *    directories, create errors and write errors.  The algorithms here
  16.  *    are set up such that alternate directories can be used in the case
  17.  *    that smail is not able to complete spooling to a primary spool
  18.  *    directory.
  19.  *
  20.  *    NOTE:  This section will probably require substantial
  21.  *           modifications to work with a non-UN*X operating
  22.  *           system.
  23.  *
  24.  *    external functions:  creat_spool, write_spool, open_spool,
  25.  *                 close_spool, unlink_spool, seek_spool,
  26.  *                 tell_spool, send_spool, read_spool,
  27.  *                 log_spool_errors, new_grade, defer_message,
  28.  *                 message_date
  29.  */
  30. #include "defs.h"
  31. #include <stdio.h>
  32. #include <ctype.h>
  33. #include <sys/types.h>
  34. #include <sys/stat.h>
  35. #include <errno.h>
  36. #include "smail.h"
  37. #include "spool.h"
  38. #include "transport.h"
  39. #include "log.h"
  40. #include "dys.h"
  41. #include "exitcodes.h"
  42. #ifndef DEPEND
  43. # include "debug.h"
  44. # include "extern.h"
  45. #endif
  46.  
  47. #if defined(POSIX_OS) || !defined(UNIX_BSD)
  48. # include <time.h>
  49. #else    /* not UNIX_BSD */
  50. # include <sys/time.h>
  51. #endif    /* not UNIX_BSD */
  52.  
  53. #if defined(UNIX_SYS5) || defined(POSIX_OS)
  54. # include <fcntl.h>
  55. #else
  56. # ifdef UNIX_BSD
  57. #  include <sys/file.h>
  58. # endif
  59. #endif
  60.  
  61. #ifdef STANDALONE
  62. # define xmalloc malloc
  63. # define xfree free
  64. extern char *malloc();
  65. int force_write_error = FALSE;
  66. #endif    /* STANDALONE */
  67.  
  68. /* variables exported from this file */
  69. char *message_id = NULL;        /* unique ID for this message */
  70. char *spool_dir;            /* directory used to spool message */
  71. char *spool_fn = NULL;            /* basename of open spool file */
  72. char *input_spool_fn = NULL;        /* name in input/ directory */
  73. int spoolfile = -1;                     /* open spool file */
  74. char *lock_fn;                /* name of lock file for spool_fn */
  75. char *msg_buf;                /* i/o buffer for spool file */
  76. char *msg_ptr;                /* read placeholder in msg_buf */
  77. char *msg_max;                /* last valid char in msg_buf */
  78. char *end_msg_buf;            /* end of msg_buf */
  79. long msg_foffset;            /* file offset for msg_buf contents */
  80. long msg_size;                /* size of spool file */
  81.  
  82. /* types local to this file */
  83. struct log_msgs {
  84.     struct log_msgs *succ;
  85.     char msg[1];
  86. };
  87. enum locker { l_creat, l_open, l_lock };    /* who is trying to lock */
  88.  
  89. /* variables local to this file */
  90. static char *temp_fn = NULL;        /* temp spool file name */
  91. static char *funct_name;        /* name of current function */
  92. static char *old_spool_dir;        /* saved value of spool_dir */
  93. static char *old_spool_fn;        /* saved value of spool_fn */
  94. static char *old_input_spool_fn;    /* saved value of input_spool_fn */
  95. static int old_spoolfile;        /* saved value of spoolfile */
  96. static struct log_msgs *log_msgs;    /* message lines to send to log */
  97. static struct log_msgs **next_log_msg;    /* where to put next log message */
  98.  
  99. /* functions local to this file */
  100. static int int_creat_spool();
  101. static void build_spool_fn();
  102. static void build_message_id();
  103. static int set_alt_spool();
  104. static int lock_spoolfile();
  105. static void copy_old_names();
  106. static void failed_write();
  107. static int copy_old_spool();
  108. static void log_message();
  109.  
  110. /* functions imported from libc */
  111. long lseek();
  112. struct tm *localtime();
  113. struct tm *gmtime();
  114. char *ctime();
  115.  
  116. /* trap undefined open flags */
  117. #ifndef    O_RDONLY
  118. # define O_RDONLY    0
  119. #endif
  120. #ifndef O_WRONLY
  121. # define O_WRONLY    1
  122. #endif
  123. #ifndef O_RDWR
  124. # define O_RDWR        2
  125. #endif
  126.  
  127.  
  128. /*
  129.  * creat_spool - create a spool file
  130.  *
  131.  * This is external entrypoint for spool file creation.  It initializes some
  132.  * state and then calls int_creat_spool() to do the real work.
  133.  *
  134.  * If spoolfile creation succeeded, return SUCCEED.  Also, the following
  135.  * external variables will be set:
  136.  *
  137.  *    spool_dir    - the directory in which the spoolfile was created
  138.  *    spool_fn    - the basename of the generated file
  139.  *    input_spool_fn    - the name of the spool file in the input directory
  140.  *    spoolfile    - the file descriptor (used by the PUTSPOOL macro)
  141.  *    message_id    - a unique message ID computed from the filename
  142.  *    msg_buf,msg_ptr    - points to a region in which the message can be
  143.  *              written into memory
  144.  *
  145.  * In addition, the current directory will be set to spool_dir, and the file
  146.  * will be locked against premature attempts at delivery by background
  147.  * processes.
  148.  *
  149.  * If spoolfile creation failed, return FAIL.  In this case, spool_fn,
  150.  * input_spool_fn, spool_dir and message_id will all be set to NULL.
  151.  */
  152. int
  153. creat_spool()
  154. {
  155.     DEBUG(DBG_SPOOL_HI, "create_spool called\n");
  156.     funct_name = "creat_spool";
  157.     temp_fn = NULL;
  158.     spool_fn = NULL;
  159.     input_spool_fn = NULL;
  160.     message_id = NULL;
  161.     lock_fn = NULL;
  162.     spoolfile = -1;
  163.     log_msgs = NULL;
  164.     next_log_msg = &log_msgs;
  165.     msg_foffset = 0;
  166.     msg_size = 0;
  167.     if (msg_buf == NULL) {
  168.     /* allocate the spool i/o buffer if it does not yet exist */
  169.     msg_buf = xmalloc(message_bufsiz);
  170.     }
  171.     msg_max = msg_buf;            /* start at beginning of buffer */
  172.     msg_ptr = msg_buf;
  173.     end_msg_buf = msg_buf + message_bufsiz;
  174.     if (int_creat_spool() == FAIL) {
  175.     DEBUG(DBG_SPOOL_LO, "creat_spool failed!\n");
  176.     return FAIL;
  177.     }
  178.     DEBUG2(DBG_SPOOL_LO, "new spool file is %s/%s\n",
  179.        spool_dir, input_spool_fn);
  180.     return SUCCEED;
  181. }
  182.  
  183. /*
  184.  * int_creat_spool - create a spool file (internal form)
  185.  *
  186.  * try to create a spool file in either the primary or alternate spool
  187.  * directory.  The file will be locked and empty at the end of creat_spool.
  188.  *
  189.  * return SUCCEED or FAIL.  If FAIL, then the spool file and lock file
  190.  * will not exist unless they could not be unlinked.
  191.  * log_spool_errors should be called once the log files are opened, as
  192.  * well.
  193.  */
  194. static int
  195. int_creat_spool()
  196. {
  197.     int attempts = 0;            /* # open attempts in a directory */
  198.     char build_temp_fn[sizeof("input/msg.dddddddddd")];
  199.  
  200.     /* first try the primary spool directory */
  201.     spool_dir = NULL;
  202.  
  203.     /* get the first "alternate" directory, which is the primary directory */
  204.     if (set_alt_spool((char *)NULL) == FAIL) {
  205.     return FAIL;
  206.     }
  207.  
  208.     /*
  209.      * create the temporary spool file in the first spool directory
  210.      * that the open succeeds in.
  211.      */
  212.     (void) sprintf((temp_fn = build_temp_fn), "input/msg.%d", getpid());
  213.     for (;;) {
  214.     /*
  215.      * unfortunately, the errno returned by creat(2) does not
  216.      * distinguish between directory and file access errors, so
  217.      * we use the open call, which does, for systems that
  218.      * support it
  219.      *
  220.      * for systems with O_SYNC but without fsync() use O_SYNC to
  221.      * ensure that the file is on disk before we return any responses
  222.      * to the user.
  223.      */
  224. #ifdef O_EXCL
  225. #if defined(O_SYNC) && !defined(HAVE_FSYNC) && !defined(NO_SPOOL_O_SYNC)
  226.     spoolfile = open(temp_fn, O_RDWR|O_CREAT|O_EXCL|O_SYNC, spool_mode);
  227. #else
  228.     spoolfile = open(temp_fn, O_RDWR|O_CREAT|O_EXCL, spool_mode);
  229. #endif    /* O_SYNC && !HAVE_FSYNC && !NO_SPOOL_O_SYNC */
  230. #else    /* O_EXCL */
  231.     /*
  232.      * if we must use creat, then the mode should not allow writing.
  233.      * As well, we will have to reopen for reading later on in
  234.      * read_spool, if we try to read on the file without closing
  235.      * and then opening again.
  236.      */
  237.     spoolfile = creat(temp_fn, spool_mode&(~0222));
  238.     if (spoolfile < 0 && errno == EACCES) {
  239.         /* we assume that EACCES does not represent a directory error */
  240.         errno = EEXIST;
  241.     }
  242. #endif    /* O_EXCL */
  243.     if (spoolfile < 0 && errno == ENOENT) {
  244.         /* NOENT means the directory did not exist, try to make it */
  245.         DEBUG1(DBG_SPOOL_LO, "make directory %s/input\n", spool_dir);
  246.         (void) mkdir("input", auto_mkdir_mode);
  247. #ifdef O_EXCL
  248. #if defined(O_SYNC) && !defined(HAVE_FSYNC) && !defined(NO_SPOOL_O_SYNC)
  249.         spoolfile = open(temp_fn, O_RDWR|O_CREAT|O_EXCL|O_SYNC, spool_mode);
  250. #else
  251.         spoolfile = open(temp_fn, O_RDWR|O_CREAT|O_EXCL, spool_mode);
  252. #endif    /* O_SYNC && !HAVE_FSYNC && !NO_SPOOL_O_SYNC */
  253. #else
  254.         spoolfile = creat(temp_fn, spool_mode&(~0222));
  255.         if (spoolfile < 0 && errno == EACCES) {
  256.         errno = EEXIST;
  257.         }
  258. #endif
  259.     }
  260.     if (spoolfile < 0) {
  261.         if (errno == EEXIST && attempts == 0) {
  262.         /* if the spool file existed, then try to unlink it */
  263.         DEBUG2(DBG_SPOOL_MID,
  264.                "int_creat_spool: %s/input/%s exists, unlinking\n",
  265.                spool_dir, temp_fn);
  266.         attempts++;        /* only attempt unlink once */
  267.         continue;
  268.         }
  269.  
  270.         /* otherwise we need to try an alternate spool directory */
  271.         if (set_alt_spool("cannot create spool file") == FAIL) {
  272.         return FAIL;
  273.         }
  274.         attempts = 0;        /* new directory, reset try count */
  275.         continue;
  276.     }
  277.  
  278.     /*
  279.      * we have an open temp file.  Now create a lock file
  280.      * and move the temp file to its more permanent name.
  281.      */
  282.     build_spool_fn(spool_grade);
  283. #ifndef lock_fd
  284.     if (lock_spoolfile(l_creat) == FAIL) {
  285.         /* we need to try an alternate directory */
  286.         (void) close(spoolfile);
  287.         (void) unlink(temp_fn);
  288.         if (set_alt_spool("cannot lock message") == FAIL) {
  289.         return FAIL;
  290.         }
  291.         continue;
  292.     }
  293. #else /* lock_fd */
  294.     if ((lock_by_name && lock_spoolfile(l_creat) == FAIL) ||
  295.         (! lock_by_name && lock_fd(spoolfile) == FAIL))
  296.     {
  297.         (void) close(spoolfile);
  298.         (void) unlink(temp_fn);
  299.         if (set_alt_spool("cannot lock message") == FAIL) {
  300.         return FAIL;
  301.         }
  302.         continue;
  303.     }
  304. #endif /* lock_fd */
  305.     if (rename(temp_fn, input_spool_fn) < 0) {
  306.         /* why would rename fail? */
  307.         if (lock_by_name) {
  308.         (void) unlink(lock_fn);
  309.         }
  310.         (void) close(spoolfile);
  311.         (void) unlink(temp_fn);
  312.         if (set_alt_spool("failed to rename temp file") == FAIL) {
  313.         return FAIL;
  314.         }
  315.         continue;
  316.     }
  317.  
  318.     /* we have a valid locked spool file */
  319.     return SUCCEED;
  320.     }
  321. }
  322.  
  323. /*
  324.  * build_spool_fn - form the spool filename
  325.  *
  326.  * as a side effect, generate the lock filename, if needed, and
  327.  * generate the message_id.
  328.  *
  329.  * the grade_char represents a priority which is appended to the
  330.  * filename.
  331.  */
  332. static void
  333. build_spool_fn(grade_char)
  334.     int grade_char;
  335. {
  336.     /* store the actual spool file basename here */
  337.     static char fn[SPOOL_FN_LEN + 1];
  338.     /* name of spool file in input directory */
  339.     static char ifn[sizeof("input/") + SPOOL_FN_LEN];
  340.     /* store the lock file name here */
  341.     static char lfn[sizeof("lock/") + SPOOL_FN_LEN];
  342.     extern long time();
  343.     long clock = time((long *)0);
  344.     struct stat statbuf;
  345.     char a_inode[8];            /* ASCII base 62 inode number */
  346.  
  347.     /* get the inode number in base 62 information */
  348.     (void) fstat(spoolfile, &statbuf);
  349.     (void) strcpy(a_inode, base62((long)statbuf.st_ino));
  350.  
  351.     /* this is 14 chars which should work on all UN*X systems */
  352.     spool_fn = fn;
  353.     (void) sprintf(spool_fn, "%s-%s%c", base62(clock), a_inode, grade_char);
  354.  
  355.     /* use this to build the path in the input directory */
  356.     input_spool_fn = ifn;
  357.     (void) sprintf(input_spool_fn, "input/%s", spool_fn);
  358.  
  359.     /* have the message-ID computed from the computed spool file name */
  360.     build_message_id();
  361.     if (lock_by_name) {
  362.     lock_fn = lfn;
  363.     (void) sprintf(lock_fn, "lock/%s", spool_fn);
  364.     }
  365.     DEBUG2(DBG_SPOOL_HI, "build_spool_fn: try spool file %s/%s\n",
  366.        spool_dir, input_spool_fn);
  367. }
  368.  
  369. /*
  370.  * build_message_id - build message_id from the value of spool_fn
  371.  *
  372.  * the only difference is that the message-Id begins with the letter `m', to
  373.  * make it a valid local-addr vis-a-vis RFC822.
  374.  */
  375. static void
  376. build_message_id()
  377. {
  378.     static char m_id[sizeof("m") + SPOOL_FN_LEN];
  379.  
  380.     message_id = m_id;
  381.     (void) sprintf(message_id, "m%s", spool_fn);
  382. }
  383.  
  384.  
  385. /*
  386.  * lock_message - lock the current message
  387.  *
  388.  * When forking a process to deliver mail, locks are released by the
  389.  * parent process and then regained in the child process by calling
  390.  * lock_message().  If the lock cannot be regained then the child
  391.  * process should exit, assuming that some other process has decided
  392.  * to attempt delivery.  This method is necessary when using lock
  393.  * files, as pids are used to determine whether the locking process
  394.  * still exists.  It is also necessary when using the System V lockf
  395.  * call as this does not preserve locks in a child of the process that
  396.  * made the lock.
  397.  *
  398.  * Returns SUCCEED if the lock was obtained, FAIL otherwise.
  399.  */
  400. int
  401. lock_message()
  402. {
  403.     struct stat statbuf;
  404.     int success;
  405.     long offset;
  406.  
  407. #ifdef lock_fd
  408.     if (lock_by_name) {
  409.     success = lock_spoolfile(l_lock);
  410.     } else {
  411.     offset = lseek(spoolfile, 0L, 1);
  412.     (void) lseek(spoolfile, 0L, 0);
  413.     success = lock_fd(spoolfile);
  414.     (void) lseek(spoolfile, offset, 0);
  415.     }
  416. #else
  417.     success = lock_spoolfile(l_lock);
  418. #endif
  419.     if (success == FAIL) {
  420.     return FAIL;
  421.     }
  422.     (void) fstat(spoolfile, &statbuf);
  423.  
  424.     /*
  425.      * If the file has been removed, then don't process it.
  426.      */
  427.     if (statbuf.st_nlink == 0) {
  428.     DEBUG2(DBG_SPOOL_MID,
  429.            "open_spool: %s/%s: spool file was removed\n",
  430.            spool_dir, input_spool_fn);
  431.     close_spool();
  432.     return FAIL;
  433.     }
  434.     return SUCCEED;
  435. }
  436.  
  437. /*
  438.  * unlock_message - unlock the current message
  439.  *
  440.  * The parent process calls this before forking a process to perform
  441.  * delivery.  See lock_message() for an explanation.
  442.  */
  443. void
  444. unlock_message()
  445. {
  446.     long offset;
  447.  
  448. #ifdef lock_fd
  449.     if (lock_by_name) {
  450. #endif
  451.     if (lock_fn) {
  452.         DEBUG2(DBG_SPOOL_HI, "close_spool: unlinking %s/lock/%s\n",
  453.            spool_dir, lock_fn);
  454.         (void) unlink(lock_fn);
  455.     }
  456. #ifdef lock_fd
  457.     } else {
  458.     offset = lseek(spoolfile, 0L, 1);
  459.     (void) lseek(spoolfile, 0L, 0);
  460.     unlock_fd(spoolfile);
  461.     (void) lseek(spoolfile, offset, 0);
  462.     }
  463. #endif
  464. }
  465.  
  466. /*
  467.  * lock_spoolfile - create a lock file for the spool file
  468.  *
  469.  * algorithm:
  470.  * 1.  create lock file exclusively.  If this fails, goto step 3.
  471.  * 2.  write ASCII process id, followed by newline.  If this succeeds,
  472.  *     then the lock succeeded, close the file and return.
  473.  * 3.  stat the lock file
  474.  * 4.  if st_size != 0, the proceed to step 10
  475.  * 5.  The lock file is empty because the locking process has
  476.  *     not yet written out its process id, or because the
  477.  *     process or system crashed prior to the write.
  478.  *     If the current time < Oct 28, 1986, then proceed to step 7.
  479.  *     If st_ctime > the current time then proceed to step 8.
  480.  *     If st_ctime < `now' - 2 hours proceed to step 9.
  481.  * 6.  The empty lock file is too new to touch (the system could
  482.  *     be very heavely loaded) and so we simply conclude that
  483.  *     some process has it locked and go on to do something else.
  484.  * 7.  Since this version of smail did not exist on Oct 28, 1986,
  485.  *     the current time must be set wrong.  At this point
  486.  *     we give up all hope of stale lock file detection,
  487.  *     consider the file locked and go on to do something else.
  488.  * 8.  The empty lock file was created in the future so we don't
  489.  *     know if the empty lock file is stale or not.  We first
  490.  *     bring the st_ctime of the file back into reality by
  491.  *     doing a chmod (to the same permission). This forces
  492.  *     the st_ctime to be set to `now'.  Next we send the
  493.  *     following error message to the system log:
  494.  *
  495.  *       time warp on zero length lock file: lock/0571338010a72y
  496.  *
  497.  *     We consider the lock file active for now.
  498.  * 9.  The empty lock file is very likely stale.  We will
  499.  *     remove the lock file but we will still consider it
  500.  *     locked, allowing some future process to operate
  501.  * 10. The lock file contains the process id of the locking
  502.  *     process.  We open the lock file and read it to find
  503.  *     the associated pid.
  504.  *     If pid == 0, then proceed to step 13.
  505.  *     If pid is not a process, then proceed to step 12.
  506.  * 11. The process pid is a valid and currently
  507.  *     running process, and thus the lock file is really
  508.  *     valid.  At this point we go on to do something else.
  509.  * 12. The lock file is very likely stale.  We will remove the
  510.  *     lock file but we will still consider it locked, allowing
  511.  *     some future process to operate on the spool file.  We
  512.  *     now go on to do something else.
  513.  * 13. If pid == 0, then the lock file is a
  514.  *     permanent lock file.  (perhaps set by the system
  515.  *     administrator to freeze a mail message)  At this
  516.  *     point we go on to do something else.
  517.  *
  518.  * return either SUCCEED or FAIL, and set errno to EEXIST if
  519.  * the spoolfile is already locked.
  520.  */
  521. static int lock_creat();
  522. static int check_empty_lock();
  523. static int verify_lock();
  524.  
  525. static int
  526. lock_spoolfile(who)
  527.     enum locker who;            /* who is locking, open or creat */
  528. {
  529.     struct stat statbuf;        /* temp buf for stats */
  530.     int attempts = 0;
  531.     int mkdir_tried = FALSE;        /* TRUE if mkdir("lock") tried */
  532.  
  533.     DEBUG1(DBG_SPOOL_HI, "lock_spoolfile called, lock_fn = %s\n", lock_fn);
  534.     /*
  535.      * loop until we have a lock or we fail to get a lock
  536.      */
  537.     for (;;) {
  538.     /*
  539.      * try to create one
  540.      */
  541.     if (lock_creat() == FAIL) {
  542.         if (errno == ENOENT && !mkdir_tried && auto_mkdir) {
  543.         mkdir_tried = TRUE;
  544.         DEBUG1(DBG_SPOOL_LO, "make directory %s/lock\n",
  545.                spool_dir);
  546.         (void) mkdir("lock", auto_mkdir_mode);
  547.         continue;
  548.         }
  549.         if (errno != EEXIST) {
  550.         /* it failed, but not because it exists, can't handle this */
  551.         DEBUG1(DBG_SPOOL_HI, "create failed: %s\n", strerrno());
  552.         return FAIL;
  553.         }
  554.         if (who == l_creat) {
  555.         /*
  556.          * for creat_spool, no lock file should exist because
  557.          * of inode uniqueness on a filesystem.  Just unlink
  558.          * the lock file and try again (once).
  559.          */
  560.         if (attempts == 0) {
  561.             (void) unlink(lock_fn);
  562.             attempts++;
  563.             continue;
  564.         }
  565.         DEBUG3(DBG_SPOOL_MID,
  566.                "lock_spoolfile: %s/lock/%s: lock failed: %s\n",
  567.                spool_dir, lock_fn, strerrno());
  568.         return FAIL;
  569.         }
  570.     } else {
  571.         /* the create worked, great! */
  572.         return SUCCEED;
  573.     }
  574.  
  575.     if (stat(lock_fn, &statbuf) < 0) {
  576.         /* failed to stat the file, hmmm */
  577.         DEBUG3(DBG_SPOOL_LO,
  578.            "lock_spoolfile: %s/lock/%s: stat failed: %s\n",
  579.            spool_dir, lock_fn, strerrno());
  580.         return FAIL;
  581.     }
  582.  
  583.     if (statbuf.st_size == 0) {
  584.         if (check_empty_lock(&statbuf) == SUCCEED) {
  585.         errno = EEXIST;        /* lock was upheld, for now */
  586.         return FAIL;
  587.         }
  588.     } else {
  589.         if (verify_lock() == SUCCEED) {
  590.         errno = EEXIST;        /* lock was upheld */
  591.         return FAIL;
  592.         }
  593.     }
  594.  
  595.     /*
  596.      * we consider the lock to be stale, so unlink it,
  597.      * and let a future process try again.
  598.      */
  599.     (void) unlink(lock_fn);
  600.     DEBUG2(DBG_SPOOL_LO,
  601.            "lock_spoolfile: %s/lock/%s: stale lock file removed\n",
  602.            spool_dir, lock_fn);
  603.     errno = EEXIST;
  604.     return FAIL;
  605.     }
  606. }
  607.  
  608. /*
  609.  * lock_creat - try to create a lock and write into it the current pid
  610.  *
  611.  * this is only called from lock_spoolfile and returns SUCCEED or FAIL.
  612.  */
  613. static int
  614. lock_creat()
  615. {
  616.     int lfd;
  617.     char apid[BITS_PER_INT/3 + 2];    /* should hold the process id */
  618.     int ct;
  619.  
  620. #ifdef O_EXCL
  621.     lfd = open(lock_fn, O_RDWR|O_CREAT|O_EXCL, lock_mode);
  622. #else    /* O_EXCL */
  623.     /*
  624.      * it is time for the silly creat trick again
  625.      */
  626.     lfd = creat(lock_fn, lock_mode&(~0222));
  627.     if (lfd < 0 && errno == EACCES) {
  628.     errno = EEXIST;
  629.     }
  630. #endif    /* O_EXCL */
  631.     if (lfd < 0) {
  632.     /* we failed to creat the lock the file */
  633.     return FAIL;
  634.     }
  635.     (void) sprintf(apid, "%u\n", getpid());
  636.     ct = strlen(apid);
  637.     if (write(lfd, apid, ct) < ct) {
  638.     /* we failed to write the pid into the lock file, quit */
  639.     (void) unlink(lock_fn);
  640.     (void) close(lfd);
  641.     return FAIL;
  642.     }
  643.     (void) close(lfd);
  644.     return SUCCEED;
  645. }
  646.  
  647. /*
  648.  * check_empty_lock - decide if zero-length lock file is stale
  649.  *
  650.  * return SUCCEED if not stale and should be kept, FAIL otherwise.
  651.  */
  652. static int
  653. check_empty_lock(stp)
  654.     struct stat *stp;            /* statbuf from lock_spoolfile */
  655. {
  656.     struct tm *ctm;
  657.     extern long time();
  658.     long now = time((long *)0);        /* verify based on age */
  659.  
  660.     ctm = gmtime(&now);
  661.     if (ctm->tm_year <= 86 && ctm->tm_mon <= 10 && ctm->tm_mday < 28) {
  662.     /*
  663.      * it is before chongo's birthday, but on a year that must be
  664.      * in the past.  Since this version of smail has not been
  665.      * written yet, punt.
  666.      */
  667.     write_log(LOG_SYS|LOG_CONS, "machine is in a time warp, check date");
  668.     return SUCCEED;
  669.     }
  670.  
  671.     if (stp->st_ctime > now) {
  672.     /*
  673.      * file created in the future, bring it back to the present, but
  674.      * otherwise assume it is a valid lock.
  675.      */
  676.     (void) chmod(lock_fn, stp->st_mode);
  677.     write_log(LOG_SYS, "time warp on zero length lock file: %s", lock_fn);
  678.     return SUCCEED;
  679.     }
  680.     if (stp->st_ctime < now - 2*3600/*two hours in seconds*/) {
  681.     /* lock not upheld, file can be removed */
  682.     return FAIL;
  683.     }
  684.  
  685.     /* a new lock file, leave it alone for now */
  686.     DEBUG(DBG_SPOOL_HI, "new zero-length lock file, let it stand\n");
  687.     return SUCCEED;
  688. }
  689.  
  690. /*
  691.  * verify_lock - decide if non-zero lockfile is stale
  692.  *
  693.  * return SUCCEED if not stale and should be kept, FAIL otherwise.
  694.  */
  695. static int
  696. verify_lock()
  697. {
  698.     int lfd;
  699.     char rpid[BITS_PER_INT/3 + 2];
  700.     int pid = 0;
  701.     int ct;
  702.  
  703.     lfd = open(lock_fn, O_RDONLY);
  704.     if (lfd < 0) {
  705.     /* failed to open the lock file, don't consider it stale yet */
  706.     DEBUG3(DBG_SPOOL_HI, "verify_lock: %s/lock/%s: %s\n",
  707.            spool_dir, lock_fn, strerrno());
  708.     return SUCCEED;
  709.     }
  710.  
  711.     /* read in the pid */
  712.     ct = read(lfd, rpid, sizeof(rpid)-1);
  713.     if (ct <= 0) {
  714.     /* failed to read anything from the lock file, ignore it for now */
  715.     (void) close(lfd);
  716.     DEBUG3(DBG_SPOOL_HI, "verify_lock: %s/lock/%s: read faile: %s\n",
  717.            spool_dir, lock_fn, strerrno());
  718.     return SUCCEED;
  719.     }
  720.     (void) close(lfd);
  721.  
  722.     rpid[ct] = '\0';            /* firewall */
  723.     (void) sscanf(rpid, "%d", &pid);
  724.     if (pid == 0) {
  725.     /* pid is 0 or not a valid number, consider the lock valid */
  726.     DEBUG(DBG_SPOOL_HI, "verify_lock: pid==0 lock considered valid\n");
  727.     return SUCCEED;
  728.     }
  729.  
  730.     /*
  731.      * does the process exist?  before you look at the man page,
  732.      * kill(pid,0) detects process existence on all versions of
  733.      * UN*X I know of including v6 and v7
  734.      */
  735.     if (kill(pid, 0) < 0 && errno == ESRCH) {
  736.     /* process does not exist, lock is stale */
  737.     return FAIL;
  738.     }
  739.  
  740.     /* the lock is valid */
  741.     DEBUG2(DBG_SPOOL_HI, "verify_lock: %s/lock/%s: valid lock\n",
  742.        spool_dir, lock_fn);
  743.     return SUCCEED;
  744. }
  745.  
  746.  
  747. /*
  748.  * write_spool - write completed block to the spool file
  749.  *
  750.  * write the contents of the spool i/o buffer out to the spool file,
  751.  * at the current i/o position.  But otherwise don't change anything.
  752.  *
  753.  * If the write fails then try to create an alternate spool file and
  754.  * copy into it the contents previously written to the old spool file.
  755.  *
  756.  * NOTE:  writing to the spool file should be done through the macro
  757.  *      PUTSPOOL(c) defined in spool.h, except that the final write
  758.  *      should be done with a call to write_spool.  PUTSPOOL will
  759.  *      manage the various associated pointers.
  760.  */
  761. int
  762. write_spool()
  763. {
  764.     old_spool_fn = NULL;        /* set to old name if new created */
  765.     old_input_spool_fn = NULL;
  766.     old_spool_dir = NULL;
  767.     old_spoolfile = spoolfile;
  768.  
  769.     funct_name = "write_spool";
  770.     DEBUG(DBG_SPOOL_HI, "write_spool called\n");
  771.  
  772.     /*
  773.      * loop until we have successfully written the block to something,
  774.      * or until we cannot recover from past errors
  775.      */
  776.     for (;;) {
  777.     if (write(spoolfile, msg_buf, msg_max - msg_buf) == msg_max - msg_buf
  778. #ifdef STANDALONE
  779.         /* it is a bit tough to get random write errors while testing */
  780.         && (!force_write_error || (force_write_error = FALSE))
  781. #endif    /* STANDALONE */
  782.         )
  783.     {
  784.         break;
  785.     } else {
  786.         /*
  787.          * write failed, unlink the old spool file and copy contents
  788.          * to a new spool file.
  789.          */
  790.         copy_old_names();
  791.         failed_write();
  792.         for (;;) {
  793.         /* get an alternate spool directory */
  794.         if (set_alt_spool("write to spool failed") == FAIL) {
  795.             (void) close(old_spoolfile);
  796.             DEBUG(DBG_SPOOL_LO,
  797.               "write_spool: no more spool dirs, write failed\n");
  798.             return FAIL;
  799.         }
  800.  
  801.         if (int_creat_spool() == FAIL) {
  802.             (void) close(old_spoolfile);
  803.             DEBUG(DBG_SPOOL_LO,
  804.            "write_spool: can't create new spool file, write failed\n");
  805.             return FAIL;
  806.         }
  807.  
  808.         if (msg_size == 0) {
  809.             break;
  810.         } else {
  811.             int code = copy_old_spool();
  812.  
  813.             if (code == READ_FAIL) {
  814.             DEBUG(DBG_SPOOL_LO,
  815.           "write_spool: failed to copy old spool file: read failed\n");
  816.             return FAIL;
  817.             } else if (code == SUCCEED) {
  818.             break;
  819.             }
  820.             DEBUG(DBG_SPOOL_LO,
  821.          "write_spool: failed to copy old spool file: write failed\n");
  822.         }
  823.         }
  824.     }
  825.     }
  826.     if (old_spool_fn) {
  827.     /* we succeeded in copying to a new spool file, don't need these */
  828.     xfree(old_spool_fn);
  829.     xfree(old_input_spool_fn);
  830.     xfree(old_spool_dir);
  831.     }
  832.     msg_size += msg_max - msg_buf;
  833.  
  834.     return SUCCEED;
  835. }
  836.  
  837. /*
  838.  * copy_old_names - make a copy of the old spool file names
  839.  */
  840. static void
  841. copy_old_names()
  842. {
  843.     if (old_spool_fn == NULL) {
  844.     /* save a copy of the old names if needed later */
  845.     old_spool_fn = COPY_STRING(spool_fn);
  846.     old_input_spool_fn = COPY_STRING(input_spool_fn);
  847.     old_spool_dir = COPY_STRING(spool_dir);
  848.     }
  849. }
  850.  
  851. /*
  852.  * failed_write - remove old files after a write error
  853.  *
  854.  * NOTE: we aren't closing them, so the data in them can still be read.
  855.  */
  856. static void
  857. failed_write()
  858. {
  859.     if (spool_fn) {
  860.     (void) unlink(input_spool_fn);
  861.     spool_fn = NULL;
  862.     input_spool_fn = NULL;
  863.     }
  864.     if (lock_fn) {
  865.     (void) unlink(lock_fn);
  866.     }
  867. }
  868.  
  869. /*
  870.  * copy_old_spool - copy old spool file to the new one
  871.  *
  872.  * return READ_FAIL on read error, SUCCEED on success, and
  873.  * WRITE_FAIL on write error
  874.  */
  875. static int
  876. copy_old_spool()
  877. {
  878.     char *tempbuf = xmalloc(message_bufsiz);
  879.     int copied = 0;            /* count of chars copied to new file */
  880.  
  881.     /*
  882.      * previous contents exist, we will need to copy them
  883.      */
  884.     (void) lseek(old_spoolfile, 0L, 0);
  885.     while (copied < msg_size) {
  886.     register int ct;
  887.  
  888.     /* fill up as much of the temp buffer as reasaonble */
  889.     if (msg_size - copied < message_bufsiz) {
  890.         ct = msg_size - copied;
  891.     } else {
  892.         ct = message_bufsiz;
  893.     }
  894.  
  895.     ct = read(old_spoolfile, tempbuf, ct);
  896.     if (ct <= 0) {
  897.         failed_write();
  898.         log_message("read failed in copying to new file");
  899.         (void) close(spoolfile);
  900.         (void) close(old_spoolfile);
  901.         return READ_FAIL;        /* read error - can't recover */
  902.     } else {
  903.         if (write(spoolfile, tempbuf, ct) < ct) {
  904.         failed_write();
  905.         return WRITE_FAIL;    /* write error - maybe can recover */
  906.         }
  907.     }
  908.     copied += ct;
  909.     }
  910.  
  911.     return SUCCEED;            /* we have recovered */
  912. }
  913.  
  914.  
  915. /*
  916.  * open_spool - open and possibly lock the given spool file
  917.  *
  918.  * the filename is assumed to be absolute, so a chdir is done to
  919.  * the directory name, and spool_fn is set to the basename.  This
  920.  * process is destructive of the spool file string passed.
  921.  *
  922.  * As a side effect, msg_buf will be loaded with the first part of the
  923.  * spool file.
  924.  *
  925.  * return SUCCEED or FAIL.  If fail, load error with error message.
  926.  * errno will be 0 if no system error was involved.
  927.  */
  928. int
  929. open_spool(fn, lock, error)
  930.     char *fn;                /* basename of spool file */
  931.     int lock;                /* if TRUE, lock the file */
  932.     char **error;            /* return error message here */
  933. {
  934.     static char lfn[sizeof("lock/") + SPOOL_FN_LEN];
  935.     struct stat statbuf;
  936.  
  937.     DEBUG1(DBG_SPOOL_HI, "open_spool(%s) called\n", fn);
  938.     funct_name = "open_spool";
  939.     message_id = NULL;
  940.     lock_fn = NULL;
  941.     spoolfile = -1;
  942.     log_msgs = NULL;
  943.     msg_foffset = 0;
  944.     msg_size = 0;
  945.     errno = 0;
  946.     if (msg_buf == NULL) {
  947.     /* allocate the spool i/o buffer if it does not yet exist */
  948.     msg_buf = xmalloc(message_bufsiz);
  949.     }
  950.     msg_max = msg_buf;            /* start at beginning of buffer */
  951.     end_msg_buf = msg_buf + message_bufsiz;
  952.     if (fn[0] != '/') {
  953.     DEBUG1(DBG_SPOOL_LO, "open_spool: %s: relative pathname!\n", fn);
  954.     *error = "filename not absolute";
  955.     return FAIL;
  956.     }
  957.     spool_dir = fn;        /* get directory from fn */
  958.     spool_fn = rindex(fn, '/');        /* get basename from fn */
  959.     input_spool_fn = spool_fn - sizeof("/input") + 1;
  960.     spool_fn++;
  961.     if (input_spool_fn < spool_dir ||
  962.     strncmp(input_spool_fn, "/input", sizeof("/input") - 1))
  963.     {
  964.     DEBUG1(DBG_SPOOL_LO, "open_spool failed: %s: invalid spool filename",
  965.            spool_dir);
  966.     *error = "invalid spool filename";
  967.     return FAIL;
  968.     }
  969.     *input_spool_fn++ = '\0';
  970.  
  971.     if (chdir(spool_dir) < 0) {
  972.     DEBUG2(DBG_SPOOL_LO, "open_spool failed: %s: chdir failed: %s",
  973.            spool_dir, strerrno());
  974.     *error = "chdir failed";
  975.     return FAIL;
  976.     }
  977.  
  978.     /*
  979.      * attempt to open the file
  980.      *
  981.      * some of the OS locking algorithms require that the file be
  982.      * opened for writing.  For such systems, the spool_mode must have
  983.      * at least ownership write permission.
  984.      */
  985. #ifdef LOCK_REQUIRES_WRITE
  986.     if (lock && ! lock_by_name) {
  987.     spoolfile = open(input_spool_fn, O_RDWR);
  988.     } else
  989. #endif
  990.     {
  991.     spoolfile = open(input_spool_fn, O_RDONLY);
  992.     }
  993.     if (spoolfile < 0) {
  994.     DEBUG3(DBG_SPOOL_LO, "open_spool: %s/%s: open failed: %s\n",
  995.            spool_dir, input_spool_fn, strerrno());
  996.     *error = "open failed";
  997.     return FAIL;
  998.     }
  999.  
  1000.     if (lock) {
  1001. #ifndef lock_fd
  1002.     /* only lock_by_name is possible */
  1003.     (void) sprintf(lfn, "lock/%s", spool_fn);
  1004.     lock_fn = lfn;
  1005.     if (lock_spoolfile(l_open) == FAIL) {
  1006.         DEBUG2(DBG_SPOOL_MID, "open_spool: %s/%s: lock failed\n",
  1007.            spool_dir, input_spool_fn);
  1008.         (void) close(spoolfile);
  1009.         *error = "lock failed";
  1010.         return FAIL;
  1011.     }
  1012. #else /* lock_fd */
  1013.     if (lock_by_name) {
  1014.         (void) sprintf(lfn, "lock/%s", spool_fn);
  1015.         lock_fn = lfn;
  1016.         if (lock_spoolfile(l_open) == FAIL) {
  1017.         DEBUG2(DBG_SPOOL_MID, "open_spool: %s/%s: lock failed\n",
  1018.                spool_dir, input_spool_fn);
  1019.         (void) close(spoolfile);
  1020.         *error = "lock failed";
  1021.         return FAIL;
  1022.         }
  1023.     } else {
  1024.         if (lock_fd(spoolfile) == FAIL) {
  1025.         DEBUG2(DBG_SPOOL_MID, "open_spool: %s/%s: lock failed\n",
  1026.                spool_dir, input_spool_fn);
  1027.         (void) close(spoolfile);
  1028.         *error = "lock failed";
  1029.         return FAIL;
  1030.         }
  1031.     }
  1032. #endif /* lock_fd */
  1033.     }
  1034.  
  1035.     /*
  1036.      * Get statistics on spool file.
  1037.      */
  1038.     (void) fstat(spoolfile, &statbuf);
  1039.  
  1040.     /*
  1041.      * If the file has been removed, then don't process it.
  1042.      */
  1043.     if (statbuf.st_nlink == 0) {
  1044.     DEBUG2(DBG_SPOOL_MID,
  1045.            "open_spool: %s/%s: spool file was removed\n",
  1046.            spool_dir, input_spool_fn);
  1047.     close_spool();
  1048.     return FAIL;
  1049.     }
  1050.  
  1051.     /*
  1052.      * now that the file itself is locked, we need to setup the
  1053.      * message_id and initialize the msg_buf pointers so that
  1054.      * read_spool will be called the first time the GETSPOOL
  1055.      * macro is called.
  1056.      */
  1057.     build_message_id();
  1058.     if (msg_buf == NULL) {
  1059.     msg_buf = xmalloc(message_bufsiz);
  1060.     end_msg_buf = msg_buf + message_bufsiz;
  1061.     }
  1062.     msg_max = msg_buf;
  1063.     msg_ptr = msg_buf;
  1064.     (void) fstat(spoolfile, &statbuf);
  1065.     msg_size = statbuf.st_size;
  1066.  
  1067.     if (lock) {
  1068.     DEBUG2(DBG_SPOOL_LO, "opened and locked spool file %s/%s\n",
  1069.            spool_dir, input_spool_fn);
  1070.     } else {
  1071.     DEBUG2(DBG_SPOOL_LO, "opened spool file %s/%s\n",
  1072.            spool_dir, input_spool_fn);
  1073.     }
  1074.     return SUCCEED;
  1075. }
  1076.  
  1077.  
  1078. /*
  1079.  * close_spool - unlock and close the spool file
  1080.  */
  1081. void
  1082. close_spool()
  1083. {
  1084.     DEBUG(DBG_SPOOL_HI, "close_spool called\n");
  1085.     if (lock_fn) {
  1086.     DEBUG2(DBG_SPOOL_HI, "close_spool: unlinking %s/lock/%s\n",
  1087.            spool_dir, lock_fn);
  1088.     (void) unlink(lock_fn);
  1089.     }
  1090.     (void) close(spoolfile);
  1091.     spoolfile = -1;
  1092.     spool_fn = NULL;
  1093.     input_spool_fn = NULL;
  1094. }
  1095.  
  1096. /*
  1097.  * unlink_spool - unlock, close and unlink the spool file
  1098.  */
  1099. void
  1100. unlink_spool()
  1101. {
  1102.     char *save_input_spool_fn = input_spool_fn;
  1103.  
  1104.     DEBUG(DBG_SPOOL_HI, "unlink_spool called\n");
  1105.     if (save_input_spool_fn) {
  1106.     DEBUG2(DBG_SPOOL_HI, "unlink_spool: unlinking: %s/%s\n",
  1107.            spool_dir, save_input_spool_fn);
  1108.     (void) unlink(save_input_spool_fn);
  1109.     }
  1110.     close_spool();
  1111. }
  1112.  
  1113. /*
  1114.  * defer_message - put the spool file in a defer directory
  1115.  *
  1116.  * some processing errors may need attention from the postmaster,
  1117.  * and may be correctable through a change in configuration, or other
  1118.  * such things.  Rather than mail such problems to the sender or to
  1119.  * the postmaster, put them in a special (error/) directory.  When
  1120.  * the problem that caused the error is resolved, the postmaster can
  1121.  * then simply move the spool file back into the spool directory and
  1122.  * delivery will be reattempted.
  1123.  */
  1124. void
  1125. defer_message()
  1126. {
  1127.     char error_fn[sizeof("error/") + SPOOL_FN_LEN];
  1128.     int success;
  1129.     char *save_input_spool_fn = input_spool_fn;
  1130.     char *save_spool_fn = spool_fn;
  1131.  
  1132.     (void) sprintf(error_fn, "error/%s", spool_fn);
  1133.     spool_fn = NULL;
  1134.     success = link(save_input_spool_fn, error_fn);
  1135.     if (success < 0 && auto_mkdir && errno == ENOENT) {
  1136.     DEBUG1(DBG_SPOOL_LO, "make directory %s/error\n", spool_dir);
  1137.     (void) mkdir("error", auto_mkdir_mode);
  1138.     success = link(save_input_spool_fn, error_fn);
  1139.     }
  1140.     if (success < 0) {
  1141.     write_log(LOG_PANIC|LOG_SYS,
  1142.           "defer_message: error linking %s/%s to error/%s: %s",
  1143.           spool_dir, save_input_spool_fn, spool_fn, strerrno());
  1144.     } else {
  1145.     if (errfile) {
  1146.         write_log(LOG_TTY|LOG_SYS, "mail moved to %s/error/%s",
  1147.               spool_dir, save_spool_fn);
  1148.     }
  1149.     input_spool_fn = save_input_spool_fn;
  1150.     spool_fn = save_spool_fn;
  1151.     unlink_spool();
  1152.     }
  1153. }
  1154.  
  1155.  
  1156. /*
  1157.  * seek_spool - seek to the specified absolute spool file offset
  1158.  *
  1159.  * causes the aligned block around that point to be read into
  1160.  * msg_buf.
  1161.  */
  1162. int
  1163. seek_spool(offset)
  1164.     register long offset;
  1165. {
  1166.     DEBUG1(DBG_SPOOL_HI, "seek_spool(%ld) called\n", offset);
  1167.     if (offset > msg_size) {
  1168.     /* attempt to seek past the end of message */
  1169.     msg_ptr = msg_max + 1;        /* will cause EOF from GETSPOOL */
  1170.     DEBUG(DBG_SPOOL_LO, "seek_spool failed, seek past end of file\n");
  1171.     return FAIL;
  1172.     }
  1173.  
  1174.     /* is the offset in the current block? */
  1175.     if (msg_foffset <= offset && offset < msg_foffset + (msg_max - msg_buf)) {
  1176.     /* yes, just adjust the current loc pointer */
  1177.     msg_ptr = msg_buf + (offset - msg_foffset);
  1178.     return SUCCEED;
  1179.     }
  1180.  
  1181.     /* compute the beginning of the block containing the offset */
  1182.     msg_foffset = offset - offset%message_bufsiz;
  1183.  
  1184.     /* actually read in the message */
  1185.     (void) lseek(spoolfile, msg_foffset, 0);
  1186.     /* set the buffer to zero-length so read_spool won't advance foffset */
  1187.     msg_max = msg_buf;
  1188.     if (read_spool() == FAIL) {
  1189.     return FAIL;
  1190.     }
  1191.  
  1192.     /* set up the ptr to point to the byte for the offset */
  1193.     msg_ptr = msg_buf + (offset - msg_foffset);
  1194.     if (msg_ptr > msg_max) {
  1195.     DEBUG(DBG_SPOOL_LO, "seek_spool failed, read came up short\n");
  1196.     return FAIL;
  1197.     }
  1198.     return SUCCEED;
  1199. }
  1200.  
  1201. /*
  1202.  * tell_spool - return the current offset in the spool file.
  1203.  */
  1204. int
  1205. tell_spool()
  1206. {
  1207.     return msg_foffset + msg_ptr - msg_buf;
  1208. }
  1209.  
  1210.  
  1211. /*
  1212.  * send_spool - write the spool file to a stdio FILE pointer
  1213.  *
  1214.  * send the spool file to an open file, starting at the current
  1215.  * offset and ending at the end of the spool file.  If PUT_DOTS is set
  1216.  * then use the hidden dot algorithm, prepending a dot to each
  1217.  * line that begins with a dot.  If PUT_CRLF is set than put a carriage
  1218.  * return before each newline.  If UNIX_FROM_HACK then put > in front of
  1219.  * any line beginning with From.
  1220.  *
  1221.  * return SUCCEED, READ_FAIL or WRITE_FAIL.
  1222.  */
  1223. int
  1224. send_spool(f, flags)
  1225.     register FILE *f;            /* file to write on */
  1226.     long flags;                /* transport flags */
  1227. {
  1228.     register char *p;
  1229.     int eof = FALSE;            /* TRUE if found end of file */
  1230.     register int last_c = '\n';
  1231.     int dot = flags & PUT_DOTS;
  1232.     int crlf = flags & PUT_CRLF;
  1233.     int uucp_hack = flags & UNIX_FROM_HACK;
  1234.  
  1235.     DEBUG(DBG_SPOOL_HI, "send_spool called\n");
  1236.     while (!eof) {
  1237.     for (p = msg_ptr; p < msg_max; p++) {
  1238.         if (last_c == '\n') {
  1239.         if (dot && *p == '.') {
  1240.             putc('.', f);        /* hidden dot algorithm */
  1241.         }
  1242.         if (uucp_hack && *p == 'F') {
  1243.             int ct = msg_max - p;
  1244.  
  1245.             /* look ahead to see if this line begins with From */
  1246.             if (ct > sizeof("From ") - 1) {
  1247.             if (strncmp("From ", p, sizeof("From ") - 1) == 0) {
  1248.                 putc('>', f);
  1249.             }
  1250.             } else if (strncmp("From ", p, ct) == 0) {
  1251.             /*
  1252.              * we have to look ahead to the next block to
  1253.              * determine if this is a From, but make sure
  1254.              * there is another block, first.
  1255.              */
  1256.             if (msg_foffset + (msg_max - msg_buf) < msg_size) {
  1257.                 int i;
  1258.  
  1259.                 msg_ptr = p;
  1260.                 if (read_spool() == FAIL) {
  1261.                 return READ_FAIL;
  1262.                 }
  1263.                 p = msg_ptr;
  1264.                 if (strncmp("From " + ct, p,
  1265.                     sizeof("From ") - ct - 1) == 0)
  1266.                 {
  1267.                 putc('>', f);
  1268.                 }
  1269.                 for (i = 0; i < ct; i++) {
  1270.                 putc("From "[i], f);
  1271.                 }
  1272.             }
  1273.             }
  1274.         }
  1275.         }
  1276.         if (*p == '\n' && crlf) {
  1277.         putc('\r', f);        /* ARPAnet CR/LF */
  1278.         }
  1279.         if (putc(last_c = *p, f) == EOF) {
  1280.         DEBUG(DBG_SPOOL_LO, "send_spool: write failed\n");
  1281.         return WRITE_FAIL;
  1282.         }
  1283.     }
  1284.     msg_ptr = p;
  1285.     if (msg_foffset + (msg_max - msg_buf) < msg_size) {
  1286.         if (read_spool() == FAIL) {
  1287.         return READ_FAIL;
  1288.         }
  1289.     } else {
  1290.         eof = TRUE;
  1291.     }
  1292.     }
  1293.  
  1294.     if (last_c != '\n') {
  1295.     if (crlf) {
  1296.         putc('\r', f);
  1297.     }
  1298.     if (putc('\n', f) == EOF) {
  1299.         DEBUG(DBG_SPOOL_LO, "send_spool: write failed\n");
  1300.         return WRITE_FAIL;
  1301.     }
  1302.     }
  1303.  
  1304.     return SUCCEED;
  1305. }
  1306.  
  1307.  
  1308. /*
  1309.  * read_spool - read in a block from the spool file
  1310.  *
  1311.  * read a block starting at the current file position into msg_buf
  1312.  * and update the various associated variables.
  1313.  *
  1314.  * return SUCCEED or FAIL.
  1315.  */
  1316. int
  1317. read_spool()
  1318. {
  1319.     register int ct;
  1320.  
  1321.     /* advance the current file offset by the amount previously read */
  1322.     msg_foffset += msg_max - msg_buf;
  1323.     if (message_bufsiz > msg_size - msg_foffset) {
  1324.     /* the remaining size is less then a whole buffer */
  1325.     ct = msg_size - msg_foffset;
  1326.     if (ct < 0) {
  1327.         errno = ENXIO;        /* reading beyond end of the file */
  1328.         DEBUG(DBG_SPOOL_LO, "read_spool: read failed, past end of file");
  1329.         return FAIL;
  1330.     }
  1331.     if (ct == 0) {
  1332.         /* end of file */
  1333.         msg_max = msg_ptr = msg_buf;
  1334.         DEBUG(DBG_SPOOL_MID, "end of file on spool file\n");
  1335.         return FAIL;
  1336.     }
  1337.     } else {
  1338.     /* there is enough data to fit in a complete buffer */
  1339.     ct = message_bufsiz;
  1340.     }
  1341.  
  1342.     /* now, where were we? */
  1343.     (void) lseek(spoolfile, msg_foffset, 0);
  1344.  
  1345.     /* read in a buffer */
  1346.     ct = read(spoolfile, msg_buf, ct);
  1347.     if (ct < 0) {
  1348.     DEBUG1(DBG_SPOOL_LO, "read_spool: read of spool file failed: %s\n",
  1349.            strerrno());
  1350.     return FAIL;
  1351.     }
  1352.  
  1353.     /* point to the end and beginning of the used region */
  1354.     msg_max = msg_buf + ct;
  1355.     msg_ptr = msg_buf;
  1356.  
  1357.     return SUCCEED;
  1358. }
  1359.  
  1360.  
  1361. /*
  1362.  * log_spool_errors - write spooling errors to system log
  1363.  *
  1364.  * while spooling, errors are not written out to the spool files.  This
  1365.  * avoid having to deal with the fact that those writes may fail.  Thus,
  1366.  * when finished spooling the message, we must write out any errors to the
  1367.  * panic file.
  1368.  */
  1369. void
  1370. log_spool_errors()
  1371. {
  1372.     struct log_msgs *next;
  1373.  
  1374.     DEBUG(DBG_SPOOL_HI, "log_spool_errors called, saving errors from spool\n");
  1375.     while (log_msgs) {
  1376.     next = log_msgs->succ;
  1377.     write_log(LOG_PANIC, "%s", log_msgs->msg);
  1378.     xfree((char *)log_msgs);
  1379.     log_msgs = next;
  1380.     }
  1381. }
  1382.  
  1383. /*
  1384.  * log_message - save a message to be written to the log file
  1385.  *
  1386.  * the message is not in printf format, but filename and directory
  1387.  * information will be added to it.
  1388.  */
  1389. static void
  1390. log_message(m)
  1391.     char *m;                /* message */
  1392. {
  1393.     char *p, *q;            /* temp */
  1394.     register unsigned a;        /* how much to alloc */
  1395.     char *save;
  1396.  
  1397.     a = sizeof(*log_msgs) + strlen(m);
  1398.     if (spool_dir) {
  1399.     a += sizeof(", dir=") + strlen(spool_dir);
  1400.     }
  1401.     if (input_spool_fn) {
  1402.     a += sizeof(", spoolfile=") + strlen(input_spool_fn);
  1403.     }
  1404.     if (lock_fn) {
  1405.     a += sizeof(", lockfile=") + strlen(lock_fn);
  1406.     }
  1407.     *next_log_msg = (struct log_msgs*)xmalloc(a);
  1408.     (*next_log_msg)->succ = NULL;
  1409.     save = p = (*next_log_msg)->msg;
  1410.     next_log_msg = &(*next_log_msg)->succ;
  1411.     /* copy message */
  1412.     for (q = m; *q; *p++ = *q++) ;
  1413.     if (spool_dir) {
  1414.     /* put spool directory in message */
  1415.     for (q = ", dir="; *q; *p++ = *q++) ;
  1416.     for (q = spool_dir; *q; *p++ = *q++) ;
  1417.     }
  1418.     if (input_spool_fn) {
  1419.     /* put spoolfile name in message */
  1420.     for (q = ", spoolfile="; *q; *p++ = *q++) ;
  1421.     for (q = input_spool_fn; *q; *p++ = *q++) ;
  1422.     }
  1423.     if (lock_fn) {
  1424.     /* put lockfile name in message */
  1425.     for (q = ", lockfile="; *q; *p++ = *q++) ;
  1426.     for (q = lock_fn; *q; *p++ = *q++) ;
  1427.     }
  1428.     *p++ = '\0';
  1429.     DEBUG2(DBG_SPOOL_MID, "%s: %s\n", funct_name, save);
  1430. }
  1431.  
  1432.  
  1433. /*
  1434.  * set_alt_spool - set a primary or alternate spool directory
  1435.  *
  1436.  * step through the list of spool directories, advancing once per
  1437.  * call to set_alt_spool and resetting when the current spool directory
  1438.  * was not set by set_alt_spool.  Do a chdir(2) to the directory
  1439.  * set spool_dir and return SUCCEED on the first one for chdir
  1440.  * succeeds.  If none remain, return FAIL.
  1441.  */
  1442. static int
  1443. set_alt_spool(m)
  1444.     char *m;                /* message to put in log */
  1445. {
  1446.     static char altname[SIZE_FILNAM];
  1447.     static char *altlist = NULL;
  1448.     char *p;
  1449.  
  1450.     /* log the message using the old spool dir then set the new one */
  1451.     if (m) {
  1452.     log_message(m);
  1453.     }
  1454.  
  1455.     if (spool_dir != altname) {
  1456.     /* previous spool dir not one of ours, reset to start */
  1457.     altlist = spool_dirs;
  1458.     }
  1459.     spool_dir = altname;
  1460.  
  1461.     /* loop until we have a valid alternate spool directory, or none left */
  1462.     while (altlist && *altlist) {
  1463.     /* copy a directory name into altname, may end in ':' or nul */
  1464.     for (p = altname; *altlist && *altlist != ':'; *p++ = *altlist++) ;
  1465.     if (*altlist == ':') {
  1466.         /* next try gets the next directory */
  1467.         altlist++;
  1468.     }
  1469.     *p++ = '\0';            /* terminate directory name */
  1470.  
  1471.     if (altname[0] != '/') {
  1472.         /* directory must begin with '/', ignore it */
  1473.         log_message("config error: alt directory doesn't begin with /");
  1474.         continue;
  1475.     }
  1476.  
  1477.     if (chdir(altname) < 0) {
  1478.         /* Okay, try to make the directory and then chdir again */
  1479.         if (auto_mkdir && errno == ENOENT) {
  1480.         DEBUG1(DBG_SPOOL_LO, "make directory %s\n", altname);
  1481.         (void) mkdir(altname, auto_mkdir_mode);
  1482.         if (chdir(altname) < 0) {
  1483.             log_message("chdir failed");
  1484.             continue;
  1485.         }
  1486.         } else {
  1487.         log_message("chdir failed");
  1488.         continue;
  1489.         }
  1490.     }
  1491.  
  1492.     /* we found a valid alternate directory */
  1493.     return SUCCEED;
  1494.     }
  1495.  
  1496.     /* we failed, *sigh* */
  1497.     return FAIL;
  1498. }
  1499.  
  1500. /*
  1501.  * new_grade - assign a new grade to the spool file
  1502.  *
  1503.  * assign a new grade, or precedence, code to the spool file.  This
  1504.  * involves linking to a new name with a different precedence character.
  1505.  * We go through build_spool_fn to compute new names and keep trying
  1506.  * new names until we have one that doesn't already exist.  If links
  1507.  * fail for some reason other than a name not existing then don't bother
  1508.  * trying to change the grade.
  1509.  *
  1510.  * return SUCCEED or FAIL, though a FAIL should be ignored as there is
  1511.  * no good way to recover and it is not generally that important.
  1512.  */
  1513. int
  1514. new_grade(grade)
  1515.     int grade;                /* grade character */
  1516. {
  1517.     /*
  1518.      * NOTE: the above must be the same size as the names
  1519.      * built by build_spool_fn
  1520.      */
  1521.     char save_spool_fn[SPOOL_FN_LEN + 1];
  1522.     char save_input_spool_fn[sizeof("input/") + SPOOL_FN_LEN + 1];
  1523.     char save_message_id[SPOOL_FN_LEN + 2];
  1524.     char save_lock_fn[sizeof("lock/") + SPOOL_FN_LEN + 1];
  1525.  
  1526.     DEBUG1(DBG_SPOOL_HI, "new_grade(%c) called\n", grade);
  1527.     /*
  1528.      * make a copy of the old names to use as args to link, and so that
  1529.      * the old names can be restored
  1530.      */
  1531.     (void) memcpy(save_spool_fn, spool_fn, sizeof(save_spool_fn));
  1532.     (void) memcpy(save_input_spool_fn, input_spool_fn,
  1533.           sizeof(save_input_spool_fn));
  1534.     (void) memcpy(save_message_id, message_id, sizeof(save_message_id));
  1535.     if (lock_by_name) {
  1536.     (void) memcpy(save_lock_fn, lock_fn, sizeof(save_lock_fn));
  1537.     }
  1538.  
  1539.     /*
  1540.      * loop until we link both lock file and spool file to new names
  1541.      * and unlink the old ones.
  1542.      */
  1543.     for (;;) {
  1544.     build_spool_fn(grade);
  1545.  
  1546.     /* try to link the lock file first so that it is initially locked */
  1547.     if (lock_by_name && link(save_lock_fn, lock_fn) < 0) {
  1548.         if (errno != EEXIST) {
  1549.         DEBUG1(DBG_SPOOL_LO, "new_grade: failed to link lock: %s\n",
  1550.                strerrno());
  1551.         break;
  1552.         }
  1553.         DEBUG1(DBG_SPOOL_MID,
  1554.            "new_grade: failed to link lock:%, try another\n",
  1555.            strerrno());
  1556.         continue;
  1557.     }
  1558.  
  1559.     /* now try to link the actual spool file */
  1560.     if (link(save_input_spool_fn, input_spool_fn) < 0) {
  1561.         if (errno != EEXIST) {
  1562.         DEBUG1(DBG_SPOOL_LO, "new_grade: failed to link spool: %s\n",
  1563.                strerrno());
  1564.         if (lock_by_name) {
  1565.             (void) unlink(lock_fn);
  1566.         }
  1567.         break;
  1568.         }
  1569.         DEBUG1(DBG_SPOOL_MID,
  1570.            "new_grade: failed to link spool file: %s, try another\n",
  1571.            strerrno());
  1572.         continue;
  1573.     }
  1574.  
  1575.     /* success, now unlink the old names */
  1576.     (void) unlink(save_input_spool_fn);
  1577.     if (lock_by_name) {
  1578.         (void) unlink(save_lock_fn);
  1579.     }
  1580.     return SUCCEED;
  1581.     }
  1582.  
  1583.     /* we failed, copy the old names back */
  1584.     (void) memcpy(spool_fn, save_spool_fn, sizeof(save_spool_fn));
  1585.     (void) memcpy(input_spool_fn, save_input_spool_fn,
  1586.           sizeof(save_input_spool_fn));
  1587.     (void) memcpy(message_id, save_message_id, sizeof(save_message_id));
  1588.     if (lock_by_name) {
  1589.     (void) memcpy(lock_fn, save_lock_fn, sizeof(save_lock_fn));
  1590.     }
  1591.  
  1592.     return FAIL;
  1593. }
  1594.  
  1595. /*
  1596.  * message_date - return the date that the message was spooled
  1597.  *
  1598.  * The message spool date is encoded in the actual Message Id as a
  1599.  * base 62 number.  Return the date as the number of seconds since
  1600.  * the epoch.
  1601.  */
  1602. long
  1603. message_date()
  1604. {
  1605.     long clock;
  1606.     register int digit;
  1607.     register char *p;
  1608.  
  1609.     clock = 0;
  1610.     for (p = message_id + 1; *p; p++) {
  1611.     if (isdigit(*p)) {
  1612.         digit = *p - '0';
  1613.     } else if (isupper(*p)) {
  1614.         digit = *p - ('A' - 10);
  1615.     } else if (islower(*p)) {
  1616.         digit = *p - ('a' - 36);
  1617.     } else {
  1618.         break;
  1619.     }
  1620.     clock = (clock * 62) + digit;
  1621.     }
  1622.     return clock;
  1623. }
  1624.  
  1625.  
  1626. #ifdef STANDALONE
  1627.  
  1628. #include "varargs.h"
  1629.  
  1630. char *program = "spool";
  1631. char *spool_dirs = "/usr/spool/smail:/wd1h/spool/smail:/usr/tmp/spool/smail";
  1632. int debug = 0;
  1633. int message_bufsiz = MESSAGE_BUF_SIZE;
  1634. int spool_mode = 0444;
  1635. int spool_grade = 'C';
  1636. int lock_mode = 0444;
  1637.  
  1638. /*
  1639.  * standalone main for spooling subsystem.
  1640.  * usage:
  1641.  *    spool [-ddebug] [-msize] o|c[w][u] [filename]
  1642.  *
  1643.  * -d sets the debug level, -m sets message_bufsiz.  An arg of `o'
  1644.  * causes a spool file in the given filename to be locked, written to
  1645.  * standard out and closed.  An arg of `c' causes a spool file to be
  1646.  * read from standard input and put in a new spool file.  If `w'
  1647.  * follows `o' or `c' then write the spool file to standard out before
  1648.  * closing.  If `u' then unspool the message.
  1649.  */
  1650. void
  1651. main(argc, argv)
  1652.     int argc;
  1653.     char *argv[];
  1654. {
  1655.     char *p;
  1656.     int mode = 0;
  1657.     int dowrite = FALSE;
  1658.     int dounspool = FALSE;
  1659.  
  1660.     program = *argv++;
  1661.     --argc;
  1662.  
  1663.     while ((*argv)[0] == '-') {
  1664.     switch ((*argv)[1]) {
  1665.     case 'd':
  1666.         debug = atoi(*argv + 2);
  1667.         break;
  1668.     case 'm':
  1669.         message_bufsiz = atoi(*argv + 2);
  1670.         break;
  1671.     default:
  1672.         usage();
  1673.         /*NOTREACHED*/
  1674.     }
  1675.     argv++;
  1676.     --argc;
  1677.     }
  1678.  
  1679.     if (argc <= 0) {
  1680.     usage();
  1681.     /*NOTREACHED*/
  1682.     }
  1683.     for (p = *argv++, --argc; *p; p++) {
  1684.     switch (*p) {
  1685.     case 'o':
  1686.         if (mode) {
  1687.         usage();
  1688.         /*NOTREACHED*/
  1689.         }
  1690.         mode = 'o';
  1691.         break;
  1692.     case 'c':
  1693.         if (mode) {
  1694.         usage();
  1695.         /*NOTREACHED*/
  1696.         }
  1697.         mode = 'c';
  1698.         break;
  1699.     case 'w':
  1700.         dowrite = TRUE;
  1701.         break;
  1702.     case 'u':
  1703.         dounspool = TRUE;
  1704.         break;
  1705.     default:
  1706.         usage();
  1707.         /*NOTREACHED*/
  1708.     }
  1709.     }
  1710.     if (!mode) {
  1711.     usage();
  1712.     /*NOTREACHED*/
  1713.     }
  1714.     if (mode == 'o' && argc <= 0) {
  1715.     (void) fprintf(stderr, "%s: open mode requires filename\n", program);
  1716.     exit(EX_USAGE);
  1717.     }
  1718.     if (mode == 'o') {
  1719.     if (open_spool(*argv) < 0) {
  1720.         (void) fprintf(stderr, "%s: open failed\n", program);
  1721.         exit(EX_OSFILE);
  1722.     }
  1723.     } else {
  1724.     register int c;
  1725.  
  1726.     if (creat_spool(*argv) < 0) {
  1727.         (void) fprintf(stderr, "%s: creat failed\n", program);
  1728.         exit(EX_OSFILE);
  1729.     }
  1730.     while ((c = getchar()) != EOF) {
  1731.         if (c == '@') {
  1732.         force_write_error = TRUE;
  1733.         }
  1734.         if (PUTSPOOL(c) == FAIL) {
  1735.         (void) fprintf(stderr, "%s: write failed\n", program);
  1736.         exit(EX_IOERR);
  1737.         }
  1738.     }
  1739.     if (write_spool() == FAIL) {
  1740.         (void) fprintf(stderr, "%s: write failed\n", program);
  1741.     }
  1742.     log_spool_errors();
  1743.     }
  1744.  
  1745.     if (dowrite) {
  1746.     if (seek_spool(0L) != SUCCEED) {
  1747.         (void) fprintf(stderr, "seek_spool failed\n");
  1748.     }
  1749.     if (send_spool(stdout, TRUE, FALSE) != SUCCEED) {
  1750.         (void) fprintf(stderr, "seek_spool failed\n");
  1751.     }
  1752.     }
  1753.     if (dounspool) {
  1754.     unlink_spool();
  1755.     } else {
  1756.     close_spool();
  1757.     }
  1758.     exit(EX_OK);
  1759. }
  1760.  
  1761. usage()
  1762. {
  1763.     (void) fprintf(stderr, "usage: %s [-ddebug] [-msize] o|c[w][u] [file]\n",
  1764.            program);
  1765.     exit(EX_USAGE);
  1766. }
  1767.  
  1768. /*VARARGS2*/
  1769. void
  1770. write_log(log, fmt, va_alist)
  1771.     int log;                /* TRUE if to write global log file */
  1772.     char *fmt;                /* printf(3) format */
  1773.     va_dcl                              /* arguments for printf */
  1774. {
  1775.     va_list ap;
  1776.  
  1777.     va_start(ap);
  1778.     (void)fprintf(stderr, log? "PUBLIC: ": "PRIVATE: ");
  1779.     (void)vfprintf(stderr, fmt, ap);
  1780.     putc('\n', stderr);
  1781.     va_end(ap);
  1782. }
  1783.  
  1784. char *
  1785. strerrno()
  1786. {
  1787.     char errmsg[100];
  1788.  
  1789.     (void) sprintf(errmsg, "Error_%d", errno);
  1790.     return errmsg;
  1791. }
  1792.  
  1793. #endif    /* STANDALONE */
  1794.